﻿using JNPF.Logging;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
using NLog;
using PARK.CardAcct.Entitys;
using PARK.Common.Core.Manager;
using PARK.Common.Extension;
using PARK.Common.Filter;
using PARK.Consumer.Entity;
using PARK.Consumer.Entitys.Dto.ConsumRecharge;
using PARK.Consumer.Entitys.Dto.Wechat;
using PARK.Consumer.Interfaces.WechatRecharge;
using PARK.Dependency;
using PARK.DynamicApiController;
using PARK.JsonSerialization;
using PARK.System.Entitys.System;
using PARK.System.Interfaces.System;
using SqlSugar;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Linq;
using WeChat.Api.Model.CreateOrder;
using Yitter.IdGenerator;

namespace PARK.Consumer
{
    /// <summary>
    /// 充值管理服务
    /// </summary>
    [ApiDescriptionSettings(Tag = "Wechat", Name = "Recharge", Order = 200)]
    [Route("api/wechat/[controller]")]
    public class WechatRechargeService : IWechatRechargeService, IDynamicApiController, ITransient
    {

        private readonly ISqlSugarRepository<ConsumRechargeEntity> _consumRechargeRepository;
        private readonly SqlSugarScope _db;
        private readonly IUserManager _userManager;
        private readonly ISysConfigService _sysConfigService;

        /// <summary>
        /// 初始化一个<see cref="WechatRechargeService"/>类型的新实例
        /// </summary>
        public WechatRechargeService(
            ISqlSugarRepository<ConsumRechargeEntity> consumRechargeRepository,
            IUserManager userManager,
            ISysConfigService sysConfigService)
        {
            _consumRechargeRepository = consumRechargeRepository;
            _db = _consumRechargeRepository.Context;
            _userManager = userManager;
            _sysConfigService = sysConfigService;
        }

        #region 获取充值记录
        /// <summary>
        /// 获取充值记录
        /// </summary>
        /// <param name="input">请求参数</param>
        /// <returns></returns>
        [HttpGet("GetRecordList")]
        public async Task<dynamic> GetRecordList([FromQuery] RechargeListQueryInput input)
        {
            var sidx = input.sidx == null ? "billDate" : input.sidx;
            var userInfo = await _userManager.GetUserInfo();
            var _EmployeeId = "0";
            if (userInfo != null && userInfo.UserKind == 1 && !string.IsNullOrEmpty(userInfo.EmployeeId))
            {
                _EmployeeId = userInfo.EmployeeId;
            }
            //充值日期
            List<string> queryBillDate = input.billDate != null ? input.billDate.Split(',').ToObeject<List<string>>() : null;
            DateTime? startBillDate = queryBillDate != null ? Ext.GetDateTime(queryBillDate.First()) : null;
            DateTime? endBillDate = queryBillDate != null ? Ext.GetDateTime(queryBillDate.Last()) : null;
            var data = await _db.Queryable<ConsumRechargeEntity>()
                .Where(p => p.EmployeeId.Equals(_EmployeeId) && p.IsPay == 1)
                //充值日期
                .WhereIF(queryBillDate != null, p => p.BillDate >= new DateTime(startBillDate.ToDate().Year, startBillDate.ToDate().Month, startBillDate.ToDate().Day, 0, 0, 0))
                .WhereIF(queryBillDate != null, p => p.BillDate <= new DateTime(endBillDate.ToDate().Year, endBillDate.ToDate().Month, endBillDate.ToDate().Day, 23, 59, 59))
                .Select(cre => new ConsumRechargeListOutput
                {
                    id = cre.Id,
                    billCode = cre.BillCode,
                    billDate = cre.BillDate,
                    rechargeAmt = cre.RechargeAmt,
                    isImpairment = cre.IsImpairment,
                    paytype = cre.Paytype,
                    isPay = cre.IsPay,
                    cardNo = cre.CardNo,
                    departName = cre.DepartName,
                    reallName = cre.ReallName,
                    jobNo = cre.JobNo,
                    mobilePhone = cre.MobilePhone,
                    beforeRecharg = cre.BeforeRecharg,
                    afterRecharg = cre.AfterRecharg,
                }).MergeTable().OrderBy(sidx + " " + input.sort).ToPagedListAsync(input.currentPage, input.pageSize);
            return PageResult<ConsumRechargeListOutput>.SqlSugarPageResult(data);
        }
        #endregion

        #region 获取金额选项
        /// <summary>
        /// 获取金额选项
        /// </summary>
        /// <returns></returns>
        [HttpGet("GetRechargeBaseItems")]
        public async Task<dynamic> GetRechargeBaseItems()
        {
            StringBuilder dbSql = new StringBuilder("select * from consum_recharge_base a order by a.showsort asc");
            return await _db.SqlQueryable<RechargeBaseItemsOutput>(dbSql.ToString()).ToListAsync();
        }
        #endregion

        #region 充值下单
        /// <summary>
        /// 充值下单
        /// </summary>
        /// <param name="input">参数</param>
        /// <returns></returns>
        [HttpPost("AddOrder")]
        public async Task<RechargeOutput> AddOrder([FromBody] RechargeCrInput input)
        {
            var Output = new RechargeOutput()
            {
                ret = 0,
                msg = "下单失败",
                orderId = "",
                payInfo = ""
            };
            if (input.rechargeAmt <= 0)
            {
                Output.ret = 0;
                Output.msg = "充值金额输入异常";
            }
            else
            {
                var userInfo = await _userManager.GetUserInfo();
                var emp = new EmployeeEntity();
                if (input.istake>0 && !string.IsNullOrEmpty(input.takeEyeId))
                {
                    emp = await _db.Queryable<EmployeeEntity>().FirstAsync(p => p.EmployeeId == input.takeEyeId);
                }
                else
                {
                    if (userInfo != null && userInfo.UserKind == 1 && !string.IsNullOrEmpty(userInfo.EmployeeId))
                    {
                        emp = await _db.Queryable<EmployeeEntity>().FirstAsync(p => p.EmployeeId == userInfo.EmployeeId);
                    }
                }
                if (emp != null && !string.IsNullOrEmpty(emp.EmployeeId))
                {
                    #region 写入订单
                    var entity = new ConsumRechargeEntity();
                    var list = _db.Queryable<ConsumRechargeEntity>().Where(d => d.CreateDate.Value.ToString("yyyy-MM-dd").Equals(DateTime.Now.ToString("yyyy-MM-dd"))).ToList();
                    string num = "";
                    if (list != null && list.Count > 0)
                    {
                        num = (list.Count + 1).ToString();
                    }
                    else
                    {
                        num = "1";
                    }
                    num = num.PadLeft(5, '0');
                    entity.BillCode = "Rc" + DateTime.Now.ToString("yyyyMMdd") + num;
                    entity.Id = YitIdHelper.NextId().ToString();
                    entity.CardNo = emp.CardNo;
                    entity.CatCardNo = emp.CatCardNo;
                    entity.EmployeeId = emp.EmployeeId;
                    entity.DepartId = emp.DepartId;
                    entity.DepartName = emp.DepartName;
                    entity.ReallName = emp.RealName;
                    entity.JobNo = emp.JobNum;
                    entity.MobilePhone = emp.MobilePhone;
                    entity.BillDate = DateTime.Now;
                    entity.DbDate = DateTime.Now;
                    entity.CreateDate = DateTime.Now;
                    entity.CreateUser = userInfo.userId;
                    entity.CreateUserName = userInfo.userName;
                    entity.IsImpairment = 1; //增值
                    entity.Paytype = 1; //线上
                    entity.BeforeRecharg = emp.RechargeBalance; //充值前余额
                    entity.RechargeAmt = input.rechargeAmt; //充值金额
                    entity.AfterRecharg = emp.RechargeBalance + input.rechargeAmt; //充值后余额
                    entity.IsPay = 0;
                    entity.PayDate = null;
                    entity.ApiPayId = "";
                    entity.PayResult = 0; //未支付
                    entity.PayResultMsg = ""; //在线自助充值
                    entity.Summary = "在线自助充值";
                    entity.CompanyId = emp.CompanyId;
                    entity.CompanyName = emp.CompanyName;
                    //关于代充
                    entity.IsTake = input.istake > 0 ? 1 : 0;
                    entity.TakeEyeId = input.istake > 0 ? userInfo.EmployeeId : "";
                    await _db.Insertable(entity).IgnoreColumns(ignoreNullColumn: true).ExecuteCommandAsync();
                    #endregion

                    //调用微信支付下单
                    var _PayRequesEntity = Unifiedorder(input.openid, input.rechargeAmt.ToString(), entity.Id, emp.CompanyId);
                    if (_PayRequesEntity != null)
                    {
                        Output.ret = 1;
                        Output.msg = "下单成功";
                        Output.orderId = entity.Id;
                        Output.payInfo = JsonConvert.SerializeObject(_PayRequesEntity);
                    }
                    else
                    {
                        Output.ret = 0;
                        Output.msg = "下单失败";
                    }
                }
                else
                {
                    Output.ret = 0;
                    Output.msg = "下单异常,请重试";
                }
            }
            return Output;
        }
        /// <summary>
        /// 更新订单支付结果
        /// </summary>
        /// <param name="OrderId"></param>
        /// <param name="PayResult"></param>
        /// <param name="transaction_id"></param>
        /// <param name="payway"></param>
        /// <returns></returns>
        public int UpdateOrderPayStatus(string OrderId, string PayResult, string transaction_id, int payway)
        {
            var Paras = new List<SugarParameter>();
            Paras.Add(new SugarParameter("@OrderId", OrderId));
            Paras.Add(new SugarParameter("@PayResult", PayResult));
            Paras.Add(new SugarParameter("@transaction_id", transaction_id));
            Paras.Add(new SugarParameter("@upflag", null, true));
            _db.Ado.UseStoredProcedure().ExecuteCommand("P_Recharge_PayStatus", Paras);
            object _obj = Paras[3].Value;
            if (_obj != null)
                return int.Parse(_obj.ToString());
            else
                return 0;
        }
        #endregion

        #region 微信下单
        private static readonly Logger _logger = LogManager.GetCurrentClassLogger();
        /// <summary>
        /// 微信统一下单
        /// </summary>
        /// <param name="openid">用户openId</param>
        /// <param name="totalfee">支付金额</param>
        /// <param name="orderNo">订单单号</param>
        /// <param name="CompanyId">租户公司Id</param>
        /// <returns></returns>
        [NonAction]
        public PayRequesEntity Unifiedorder(string openid, string totalfee, string orderNo, string CompanyId)
        {
            var url = "https://api.mch.weixin.qq.com/pay/unifiedorder";//微信统一下单请求地址
            //获取统一下单参数
            var param = GetUnifiedOrderParam(openid, orderNo, totalfee, CompanyId);
            new LoggEx().Info("微信支付统一下单 发送参数为:" + param);
            var payRes = XDocument.Parse(param.ToString());
            var root = payRes.Element("xml");
            var res = GetPayRequestParam(root, CompanyId);
            new LoggEx().Info("微信支付统一下单 参数实体为:" + JsonConvert.SerializeObject(res));
            return res;
        }
        /// <summary>
        /// 取统一下单的请求参数
        /// </summary>
        /// <param name="openid">当前微信用户OpenId</param>
        /// <param name="orderNo">订单号</param>
        /// <param name="totalfee">支付金额</param>
        /// <param name="CompanyId">租户公司Id</param>
        /// <returns></returns>
        private string GetUnifiedOrderParam(string openid, string orderNo, string totalfee,string CompanyId)
        {
           
            string appid = _db.Queryable<SysConfigEntity>().First(s => s.Category == "WxPayConfig" && s.Key == "wx_appid" && s.CompanyId== CompanyId).Value;
            string secret = _db.Queryable<SysConfigEntity>().First(s => s.Category == "WxPayConfig" && s.Key == "wx_app_secret" && s.CompanyId == CompanyId).Value; 
            string mch_id = _db.Queryable<SysConfigEntity>().First(s => s.Category == "WxPayConfig" && s.Key == "wx_mch_id" && s.CompanyId == CompanyId).Value; 
            string ip = _db.Queryable<SysConfigEntity>().First(s => s.Category == "WxPayConfig" && s.Key == "wx_pay_svrip" && s.CompanyId == CompanyId).Value;
            string PayResulturl = _db.Queryable<SysConfigEntity>().First(s => s.Category == "WxPayConfig" && s.Key == "wx_pay_resulturl" && s.CompanyId == CompanyId).Value;
            /*
               string appid = _db.Queryable<SysConfigEntity>().First(s => s.Category == "WxPayConfig" && s.Key == "wx_appid").Value;
               string secret = _db.Queryable<SysConfigEntity>().First(s => s.Category == "WxPayConfig" && s.Key == "wx_app_secret").Value;
               string mch_id = _db.Queryable<SysConfigEntity>().First(s => s.Category == "WxPayConfig" && s.Key == "wx_mch_id").Value;
               string ip = _db.Queryable<SysConfigEntity>().First(s => s.Category == "WxPayConfig" && s.Key == "wx_pay_svrip").Value;
               string PayResulturl = _db.Queryable<SysConfigEntity>().First(s => s.Category == "WxPayConfig" && s.Key == "wx_pay_resulturl").Value;
            */
            int newtotalfee = Convert.ToInt32(Convert.ToDecimal(totalfee) * 100);

            string strcode = "就餐充值";////商品描述交易字段
            byte[] buffer = Encoding.UTF8.GetBytes(strcode);
            string body = Encoding.UTF8.GetString(buffer, 0, buffer.Length);
            var dic = new Dictionary<string, string>
                {
                    {"appid", appid},
                    {"mch_id", mch_id},
                    {"nonce_str", GetRandomString(20)},
                    {"body",body},
                    {"out_trade_no",orderNo},//商户自己的订单号码
                    {"total_fee",newtotalfee.ToString()},
                    {"spbill_create_ip",ip},//服务器的IP地址
                    {"notify_url",PayResulturl},//异步通知的地址，不能带参数
                    {"trade_type","JSAPI" },
                    {"openid",openid}
                };
            //加入签名
            dic.Add("sign", GetSignString(dic, CompanyId));
            var sb = new StringBuilder();
            sb.Append("<xml>");
            foreach (var d in dic)
            {
                sb.Append("<" + d.Key + ">" + d.Value + "</" + d.Key + ">");
            }
            sb.Append("</xml>");

            HttpClient client = new HttpClient();
            new LoggEx().Info("微信支付统一下单 发送前前参数为:" + sb.ToString());
            var bodyJson = new StringContent(sb.ToString(), Encoding.UTF8, "application/json");
            var resp = client.PostAsync("https://api.mch.weixin.qq.com/pay/unifiedorder", bodyJson).Result;
            // 注意！！！ 这个resp只是http的结果，需要把接口具体返回的值读取出来，如果接口报错的话，这地方可以看到具体的错误信息，我就是在这里入坑的。
            var respStr = resp.Content.ReadAsStringAsync().Result;

            return respStr;
            //return sb.ToString();
        }
        /// <summary>
        /// 获取返回给小程序的支付参数
        /// </summary>
        /// <param name="root"></param>
        /// <returns></returns>
        private PayRequesEntity GetPayRequestParam(XElement root,string CompanyId)
        {
            string appid = _db.Queryable<SysConfigEntity>().First(s => s.Category == "WxPayConfig" && s.Key == "wx_appid" && s.CompanyId== CompanyId).Value; //_sysConfigService.GetInfo("WxPayConfig", "wx_appid").ToString();
            if (root.Element("return_code").Value == "SUCCESS" && root.Element("result_code").Value == "SUCCESS")
            {
                var res = new Dictionary<string, string>
                {
                    {"appId", appid},
                    {"timeStamp", Convert.ToInt64((DateTime.Now - new DateTime(1970, 1, 1)).TotalSeconds).ToString()},
                    {"nonceStr", GetRandomString(20)},
                    {"package",  "prepay_id=" + root.Element("prepay_id").Value},
                    {"signType", "MD5"}
                };
                //在服务器上签名
                res.Add("paySign", GetSignString(res, CompanyId));
                var payEntity = new PayRequesEntity
                {
                    appId = res["appId"],
                    package = res["package"],
                    nonceStr = res["nonceStr"],
                    paySign = res["paySign"],
                    signType = res["signType"],
                    timeStamp = res["timeStamp"]
                };
                return payEntity;
            }
            return new PayRequesEntity();
        }
        /// <summary>
        /// 从字符串里随机得到，规定个数的字符串.
        /// </summary>
        /// <param name="CodeCount"></param>
        /// <returns></returns>
        private static string GetRandomString(int CodeCount)
        {
            string allChar = "1,2,3,4,5,6,7,8,9,A,B,C,D,E,F,G,H,i,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z";
            string[] allCharArray = allChar.Split(',');
            string RandomCode = "";
            int temp = -1;
            Random rand = new Random();
            for (int i = 0; i < CodeCount; i++)
            {
                if (temp != -1)
                {
                    rand = new Random(temp * i * ((int)DateTime.Now.Ticks));
                }
                int t = rand.Next(allCharArray.Length - 1);
                while (temp == t)
                {
                    t = rand.Next(allCharArray.Length - 1);
                }
                temp = t;
                RandomCode += allCharArray[t];
            }
            return RandomCode;
        }

        private string GetSignString(Dictionary<string, string> dic,string CompanyId)
        {
            string key = _db.Queryable<SysConfigEntity>().First(s => s.Category == "WxPayConfig" && s.Key == "wx_mch_key" && s.CompanyId== CompanyId).Value;                                                                                                     
            dic = dic.OrderBy(d => d.Key).ToDictionary(d => d.Key, d => d.Value);//排序
            //连接字段
            var sign = dic.Aggregate("", (current, d) => current + (d.Key + "=" + d.Value + "&"));
            sign += "key=" + key;
            MD5 md5 = MD5.Create();
            sign = BitConverter.ToString(md5.ComputeHash(Encoding.UTF8.GetBytes(sign))).Replace("-", null);
            return sign;
        }
        private async Task<string> PostUnifiedOrder(string payUrl, string para)
        {
            string result = string.Empty;
            try
            {
                HttpClient client = new HttpClient();
                XmlDocument doc = new XmlDocument();
                doc.LoadXml(para);
                HttpContent httpContent = new StringContent(JsonConvert.SerializeXmlNode(doc));
                httpContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
                httpContent.Headers.ContentType.CharSet = "utf-8";
                HttpResponseMessage hrm = client.PostAsync(payUrl, httpContent).Result;
                result = await hrm.Content.ReadAsStringAsync();
            }
            catch (Exception e)
            {
                result = e.Message;
            }
            return result;
        }
        #endregion

        #region 收到退款通知,更新数据
        /// <summary>
        /// 收到退款通知,更新数据
        /// </summary>
        /// <param name="redfundModel"></param>
        /// <returns></returns>
        public int UpdateOrderRedfundStatus(ReturnReFundNotifyModel redfundModel)
        {
            try
            {
                if (redfundModel!=null && !string.IsNullOrEmpty(redfundModel.refund_status))
                {
                    var Paras = new List<SugarParameter>();
                    Paras.Add(new SugarParameter("@refund_id", redfundModel.refund_id));
                    Paras.Add(new SugarParameter("@out_refund_no", redfundModel.out_refund_no));
                    Paras.Add(new SugarParameter("@refund_status", redfundModel.refund_status));                    
                    Paras.Add(new SugarParameter("@payer_refund", redfundModel.amount.payer_refund));
                    Paras.Add(new SugarParameter("@upflag", null, true));
                    _db.Ado.UseStoredProcedure().ExecuteCommand("P_PayRefund_Notify", Paras);
                    object _obj = Paras[5].Value;
                    if (_obj != null)
                        return int.Parse(_obj.ToString());
                    else
                        return 0;
                }
                else
                {
                    return 0;
                }
            }
            catch (Exception ex)
            {
                return 0;
            }
        }
        #endregion

        #region 获取代充值记录
        /// <summary>
        /// 获取代充值记录
        /// </summary>
        /// <param name="input">请求参数</param>
        /// <returns></returns>
        [HttpGet("GetTakeRechList")]
        public async Task<dynamic> GetTakeRechList([FromQuery] RechargeListQueryInput input)
        {
            var sidx = input.sidx == null ? "billDate" : input.sidx;
            var userInfo = await _userManager.GetUserInfo();
            var _EmployeeId = "0";
            if (userInfo != null && userInfo.UserKind == 1 && !string.IsNullOrEmpty(userInfo.EmployeeId))
            {
                _EmployeeId = userInfo.EmployeeId;
            }
            //充值日期
            List<string> queryBillDate = input.billDate != null ? input.billDate.Split(',').ToObeject<List<string>>() : null;
            DateTime? startBillDate = queryBillDate != null ? Ext.GetDateTime(queryBillDate.First()) : null;
            DateTime? endBillDate = queryBillDate != null ? Ext.GetDateTime(queryBillDate.Last()) : null;
            var data = await _db.Queryable<ConsumRechargeEntity>()
                //代充人员
                .Where(p => p.TakeEyeId.Equals(_EmployeeId) && p.IsPay==1)
                 //代充标记
                .Where(p => p.IsTake==1 && p.IsTake==1)
                //充值日期
                .WhereIF(queryBillDate != null, p => p.BillDate >= new DateTime(startBillDate.ToDate().Year, startBillDate.ToDate().Month, startBillDate.ToDate().Day, 0, 0, 0))
                .WhereIF(queryBillDate != null, p => p.BillDate <= new DateTime(endBillDate.ToDate().Year, endBillDate.ToDate().Month, endBillDate.ToDate().Day, 23, 59, 59))
                .Select(cre => new ConsumRechargeListOutput
                {
                    id = cre.Id,
                    billCode = cre.BillCode,
                    billDate = cre.BillDate,
                    rechargeAmt = cre.RechargeAmt,
                    isImpairment = cre.IsImpairment,
                    paytype = cre.Paytype,
                    isPay = cre.IsPay,
                    cardNo = cre.CardNo,
                    departName = cre.DepartName,
                    reallName = cre.ReallName,
                    jobNo = cre.JobNo,
                    mobilePhone = cre.MobilePhone,
                    beforeRecharg = cre.BeforeRecharg,
                    afterRecharg = cre.AfterRecharg,
                }).MergeTable().OrderBy(sidx + " " + input.sort).ToPagedListAsync(input.currentPage, input.pageSize);
            return PageResult<ConsumRechargeListOutput>.SqlSugarPageResult(data);
        }
        #endregion
    }
}
